package matchs

import (
	"context"
	"encoding/json"
	"errors"
	"time"

	"github.com/flipped-aurora/gin-vue-admin/server/global"
	"github.com/flipped-aurora/gin-vue-admin/server/model/matchs"
	"github.com/flipped-aurora/gin-vue-admin/server/model/matchs/request"
	"github.com/flipped-aurora/gin-vue-admin/server/model/matchs/response"
	"github.com/flipped-aurora/gin-vue-admin/server/model/system"
	sys "github.com/flipped-aurora/gin-vue-admin/server/service/system"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

type AskService struct{}

var AskServiceApp = new(AskService)

// CreateAskTask 创建问问任务
// @description: 创建问问任务，包括检查用户当日发布次数、创建任务记录、更新配额等操作
// @param: ctx context.Context, userId uint64, content string, targetProfessionalIDs []uint, isAnonymous int8
// @return: askId uint64, err error
func (s *AskService) CreateAskTask(ctx context.Context, userId uint64, content string, targetProfessionalIDs []uint, isAnonymous int8) (uint, error) {
	// 检查参数有效性
	if len(content) < 10 || len(content) > 500 {
		return 0, errors.New("问题内容长度需在10-500字符之间")
	}

	if len(targetProfessionalIDs) == 0 {
		return 0, errors.New("目标专业ID数组不能为空")
	}

	// 开始事务
	var askId uint
	err := global.GVA_DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		// 获取当前东八区时间
		now := time.Now().In(global.LocTime)
		year, month, day := now.Date()

		// 计算当日起始时间和次日起始时间（东八区）
		startOfDay := time.Date(year, month, day, 0, 0, 0, 0, global.LocTime)
		endOfDay := startOfDay.Add(24 * time.Hour)

		// 检查用户当日发布次数是否超过1次
		var taskCount int64
		if err := tx.Model(&matchs.MatchAskTask{}).
			Where("user_id = ? AND created_at >= ? AND created_at < ?", userId, startOfDay, endOfDay).
			Count(&taskCount).Error; err != nil {
			return err
		}

		if taskCount >= 1 {
			return errors.New("今日发布次数已达上限")
		}

		// 序列化目标专业ID数组
		professionalIDsJSON, err := json.Marshal(targetProfessionalIDs)
		if err != nil {
			return errors.New("序列化目标专业ID失败")
		}

		// 创建match_ask_task记录
		// 使用动态计算确保ExpireAt和RemainingMinutes一致
		expireAt := now.Add(24 * time.Hour)
		task := matchs.MatchAskTask{
			UserID:                userId,
			TargetProfessionalIDs: string(professionalIDsJSON),
			Content:               content,
			IsAnonymous:           isAnonymous,
			Status:                matchs.MatchAskStatusMatching,     // 初始状态为匹配中
			ExpireAt:              expireAt,                          // 默认24小时后过期
			PushedCount:           0,                                 // 推送次数初始为0
			RemainingMinutes:      int(24 * time.Hour / time.Minute), // 动态计算24小时对应的分钟数
		}

		if err := tx.Create(&task).Error; err != nil {
			return errors.New("创建问问任务失败")
		}

		askId = task.ID

		// 创建MatchLog记录，记录任务创建事件
		logPayload := map[string]interface{}{
			"content":               content,
			"isAnonymous":           isAnonymous,
			"targetProfessionalIds": targetProfessionalIDs,
			"expireAt":              expireAt,
			"remainingMinutes":      int(24 * time.Hour / time.Minute),
		}
		payloadJSON, _ := json.Marshal(logPayload)

		matchLog := matchs.MatchLog{
			TaskType: matchs.MatchRelationTaskTypeAsk, // 问问任务类型
			TaskID:   uint64(askId),
			UserID:   userId,
			Action:   matchs.MatchLogActionTaskCreated, // 任务创建事件
			Payload:  string(payloadJSON),
			// IP地址可以从上下文中获取，如果有的话
		}

		if err := tx.Create(&matchLog).Error; err != nil {
			global.GVA_LOG.Error("创建匹配日志失败", zap.Error(err))
			// 这里不返回错误，因为日志记录失败不应影响主业务流程
		}
		return nil
	})

	// 如果创建成功，则异步进行第一次匹配
	if err != nil {
		// go func
	}

	return askId, err
}

// GetUserAskTaskList 获取用户的问问任务列表
// @description: 根据用户ID和状态筛选获取问问任务列表，支持分页
// @param: ctx context.Context 上下文
// @param: userId uint64 用户ID
// @param: status *int8 任务状态（nil表示不筛选）
// @param: page int 页码
// @param: size int 每页条数
// @return: []response.AskTaskVO 任务列表
// @return: int64 总条数
// @return: error 错误信息
func (s *AskService) GetUserAskTaskList(ctx context.Context, userId uint64, status *int8, page int, size int) ([]response.AskTaskVO, int64, error) {

	// 构建查询
	db := global.GVA_DB.WithContext(ctx).Model(&matchs.MatchAskTask{}).Where("user_id = ?", userId)

	// 如果status不为nil，添加状态筛选条件
	if status != nil {
		db = db.Where("status = ?", *status)
	}

	// 查询总数
	var total int64
	if err := db.Count(&total).Error; err != nil {
		return nil, 0, errors.New("查询任务总数失败: " + err.Error())
	}

	// 分页查询任务列表
	var tasks []matchs.MatchAskTask
	// 计算偏移量
	offset := (page - 1) * size
	if err := db.Order("created_at DESC").Offset(offset).Limit(size).Find(&tasks).Error; err != nil {
		return nil, 0, errors.New("查询任务列表失败: " + err.Error())
	}

	// 转换为响应模型
	var voList []response.AskTaskVO
	for _, task := range tasks {
		// 构建响应对象，使用转换函数
		vo := response.ConvertToAskTaskVO(&task)
		voList = append(voList, vo)
	}
	return voList, total, nil
}

// GetAskTaskDetail 获取单个问问任务详情（含回复列表）
// @description: 根据任务ID获取任务详情和回复列表
// @param: ctx context.Context 上下文
// @param: userId uint64 用户ID（用于权限校验）
// @param: askId uint 问问任务ID
// @return: response.AskTaskDetailVO 任务详情
// @return: error 错误信息
func (s *AskService) GetAskTaskDetail(ctx context.Context, userId uint, askId uint) (response.AskTaskDetailVO, error) {
	// 1. 查询任务信息，附加user_id条件确保只能查看自己的任务
	var task matchs.MatchAskTask
	db := global.GVA_DB.WithContext(ctx)
	if err := db.Where("id = ? AND user_id = ?", askId, userId).First(&task).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return response.AskTaskDetailVO{}, errors.New("问问任务不存在或已删除")
		}
		global.GVA_LOG.Error("查询asks任务失败", zap.Error(err))
		return response.AskTaskDetailVO{}, errors.New("查询asks任务失败: " + err.Error())
	}

	// 2. 查询回复列表
	var replies []matchs.MatchAskReply
	if err := db.Where("task_id = ?", askId).Order("created_at DESC").Find(&replies).Error; err != nil {
		global.GVA_LOG.Error("查询回复列表失败", zap.Error(err))
		return response.AskTaskDetailVO{}, errors.New("查询回复列表失败: " + err.Error())
	}

	// 3. 转换为响应模型
	detail := response.AskTaskDetailVO{
		AskTaskVO: response.ConvertToAskTaskVO(&task),
		ReplyList: []response.AskReplyVO{}, // 初始化空数组，避免前端渲染报错
	}

	// 4. 转换回复列表
	replyVOs, err := s.convertReplyList(ctx, replies, task.SelectedReplyID)
	if err != nil {
		return response.AskTaskDetailVO{}, err
	}
	detail.ReplyList = replyVOs

	return detail, nil
}

// convertReplyList 将回复列表转换为VO列表（私有辅助函数）
func (s *AskService) convertReplyList(ctx context.Context, replies []matchs.MatchAskReply, selectedReplyID uint64) ([]response.AskReplyVO, error) {
	// 状态映射字典（私有常量）

	var replyVOs []response.AskReplyVO
	for _, reply := range replies {
		replyVO := response.AskReplyVO{
			Id:          reply.ID,
			Content:     reply.Content,
			ReplyUserId: reply.ReplyUserID,
			IsAnonymous: reply.IsAnonymous,
			IsSelected:  0, // 默认不是最佳回复
			CreatedAt:   reply.CreatedAt,
		}

		// 判断是否为最佳回复
		if selectedReplyID > 0 && selectedReplyID == uint64(reply.ID) {
			replyVO.IsSelected = 1
		}

		// 获取回复用户信息
		userInfo, err := s.getReplyUserInfo(ctx, reply.ReplyUserID, reply.IsAnonymous)
		if err != nil {
			global.GVA_LOG.Error("获取回复用户信息失败", zap.Error(err))
			// 即使获取用户信息失败，也继续处理，使用空信息
			userInfo = map[string]interface{}{}
		}
		replyVO.ReplyUserInfo = userInfo

		replyVOs = append(replyVOs, replyVO)
	}

	return replyVOs, nil
}

// getReplyUserInfo 获取回复用户信息（私有辅助函数）
func (s *AskService) getReplyUserInfo(ctx context.Context, replyUserId uint64, isAnonymous int8) (map[string]interface{}, error) {
	db := global.GVA_DB.WithContext(ctx)

	// 根据是否匿名为不同类型的用户信息
	if isAnonymous == 1 {
		// 匿名回复：获取专业信息（由于GetUserProfessionalInfo方法不存在，这里使用模拟数据）
		// 实际实现时应该调用community模块的GetUserProfessionalInfo方法
		var user system.SysUser
		if err := db.Where("id = ?", replyUserId).First(&user).Error; err != nil {
			return map[string]interface{}{"professional": "未知专业", "level": "未知等级"}, nil
		}
		// 这里简单模拟专业和等级信息
		return map[string]interface{}{
			"professional": "专业",
			"level":        "等级",
		}, nil
	} else {
		// 非匿名回复：获取昵称和头像
		var user system.SysUser
		if err := db.Where("id = ?", replyUserId).First(&user).Error; err != nil {
			return nil, errors.New("查询用户信息失败: " + err.Error())
		}
		return map[string]interface{}{
			"nickname": user.NickName,
			"avatar":   user.HeaderImg,
		}, nil
	}
}

// ReplyAskTask 回复问问任务
// @description: 回复问问任务，包括校验权限、创建回复记录、更新任务状态等操作
// @param: ctx context.Context 上下文
// @param: replyUserId uint 回复者用户ID
// @param: req *request.ReplyAskTaskReq 回复请求参数
// @return: replyId uint 回复记录ID
// @return: createdAt time.Time 创建时间
// @return: err error 错误信息
func (s *AskService) ReplyAskTask(ctx context.Context, replyUserId uint, req *request.ReplyAskTaskReq) (uint, time.Time, error) {
	// 开始事务
	var replyId uint
	var createdAt time.Time
	err := global.GVA_DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		// 1. 校验问问任务存在性
		var task matchs.MatchAskTask
		if err := tx.Where("id = ?", req.AskID).First(&task).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return errors.New("问问任务不存在或已删除")
			}
			global.GVA_LOG.Error("查询问问任务失败", zap.Error(err))
			return errors.New("查询问问任务失败")
		}

		// 2. 校验任务状态
		if task.Status >= matchs.MatchAskStatusCompleted {
			return errors.New("该任务已无法回复")
		}

		// 3. 校验回复者权限（专业匹配）
		if err := s.checkUserProfessionalMatch(ctx, replyUserId, &task); err != nil {
			return err
		}

		// 4. 校验回复次数（同一用户对同一任务只能回复1次）
		var existingReplyCount int64
		if err := tx.Model(&matchs.MatchAskReply{}).
			Where("task_id = ? AND reply_user_id = ?", req.AskID, replyUserId).
			Count(&existingReplyCount).Error; err != nil {
			global.GVA_LOG.Error("查询回复记录失败", zap.Error(err))
			return errors.New("查询回复记录失败")
		}
		if existingReplyCount > 0 {
			return errors.New("您已回复过该任务")
		}

		// 5. 创建回复记录
		reply := matchs.MatchAskReply{
			TaskID:      uint64(req.AskID),
			ReplyUserID: uint64(replyUserId),
			Content:     req.Content,
			IsAnonymous: req.IsAnonymous,
			Status:      0, // 待处理状态
			ChatID:      "",
		}

		if err := tx.Create(&reply).Error; err != nil {
			global.GVA_LOG.Error("创建回复记录失败", zap.Error(err))
			return errors.New("创建回复记录失败")
		}

		replyId = reply.ID
		createdAt = reply.CreatedAt

		// 6. 更新任务状态为已收到回复（如果当前状态为匹配中）
		if task.Status == matchs.MatchAskStatusMatching {
			if err := tx.Model(&task).Update("status", matchs.MatchAskStatusReplied).Error; err != nil {
				global.GVA_LOG.Error("更新任务状态失败", zap.Error(err))
				return errors.New("更新任务状态失败")
			}
		}

		// 7. 增加任务回复计数
		if err := tx.Model(&task).Update("reply_count", gorm.Expr("reply_count + ?", 1)).Error; err != nil {
			global.GVA_LOG.Error("更新回复计数失败", zap.Error(err))
			return errors.New("更新回复计数失败")
		}

		// 8. 创建匹配日志记录
		logPayload := map[string]interface{}{
			"content":     req.Content,
			"isAnonymous": req.IsAnonymous,
			"taskId":      req.AskID,
		}
		payloadJSON, _ := json.Marshal(logPayload)

		matchLog := matchs.MatchLog{
			TaskType: matchs.MatchRelationTaskTypeAsk,
			TaskID:   uint64(req.AskID),
			UserID:   uint64(replyUserId),
			Action:   matchs.MatchLogActionAskReplied, // 使用正确的常量
			Payload:  string(payloadJSON),
		}

		if err := tx.Create(&matchLog).Error; err != nil {
			global.GVA_LOG.Error("创建匹配日志失败", zap.Error(err))
			// 日志记录失败不影响主业务
		}

		return nil
	})

	return replyId, createdAt, err

}

// checkUserProfessionalMatch 校验用户专业是否匹配
func (s *AskService) checkUserProfessionalMatch(ctx context.Context, replyUserId uint, task *matchs.MatchAskTask) error {
	// 1. 检查用户是否已完成专业认证
	isAuth, err := sys.UserServiceApp.CheckUserMajorAuthStatus(uint(replyUserId))
	if err != nil {
		global.GVA_LOG.Error("获取用户专业认证状态失败", zap.Error(err))
		return errors.New("获取用户专业认证状态失败")
	}
	if !isAuth {
		return errors.New("您尚未完成专业认证，无法回复")
	}

	// 2. 获取用户信息，获取专业ID
	user, err := sys.UserServiceApp.GetUserByID(uint(replyUserId))
	if err != nil {
		global.GVA_LOG.Error("获取用户信息失败", zap.Error(err))
		return errors.New("获取用户信息失败")
	}

	if user.Major == 0 {
		return errors.New("您的专业信息未完善")
	}

	// 3. 反序列化目标专业ID数组
	var targetProfessionalIDs []uint
	if err := json.Unmarshal([]byte(task.TargetProfessionalIDs), &targetProfessionalIDs); err != nil {
		global.GVA_LOG.Error("解析目标专业ID失败", zap.Error(err))
		return errors.New("解析目标专业ID失败")
	}

	// 4. 校验用户专业是否在目标专业列表中
	for _, pid := range targetProfessionalIDs {
		if uint(user.Major) == pid {
			return nil // 专业匹配
		}
	}

	return errors.New("您的专业不匹配，无法回复")
}

// GetReceivedAskTaskList 获取用户接收到的问问任务列表
// @description: 查询用户接收到的问问任务，支持状态筛选和分页
// @param: ctx context.Context 上下文
// @param: userId uint64 用户ID（接收者）
// @param: statusFilter string 状态筛选（unreplied/replied/selected/expired）
// @param: page int 页码
// @param: pageSize int 每页条数
// @return: []map[string]interface{} 任务列表
// @return: int64 总条数
// @return: error 错误信息
func (s *AskService) GetReceivedAskTaskList(ctx context.Context, userId uint64, statusFilter string, page int, pageSize int) ([]map[string]interface{}, int64, error) {
	// 构建查询
	db := global.GVA_DB.WithContext(ctx)

	// 使用子查询获取当前用户接收到的问问任务
	var total int64
	query := db.Table("match_ask_push as push").
		Select(
			"task.id as askId",
			"task.user_id as askerId",
			"task.content",
			"task.is_anonymous as askerIsAnonymous",
			"task.expire_at as expireTime",
			"push.pushed_at as pushTime",
			"push.status as pushStatus",
			"reply.content as replyContent",
			"reply.id as replyId",
			"task.selected_reply_id",
		).
		Joins("LEFT JOIN match_ask_task as task ON push.ask_task_id = task.id").
		Joins("LEFT JOIN match_ask_reply as reply ON push.ask_task_id = reply.task_id AND reply.reply_user_id = push.receive_user_id").
		Where("push.receive_user_id = ?", userId)

	// 状态筛选
	switch statusFilter {
	case "unreplied":
		query = query.Where("push.status = ? AND task.expire_at > ?", 0, time.Now().In(global.LocTime)) // 未回复且未过期
	case "replied":
		query = query.Where("push.status = ?", 1) // 已回复
	case "selected":
		query = query.Where("reply.id = task.selected_reply_id AND reply.reply_user_id = ?", userId) // 已被选中
	case "expired":
		query = query.Where("task.expire_at <= ? AND push.status != ?", time.Now().In(global.LocTime), 1) // 已过期且未回复
	}

	// 查询总数
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, errors.New("查询任务总数失败: " + err.Error())
	}

	// 分页查询
	var results []map[string]interface{}
	offset := (page - 1) * pageSize
	if err := query.Order("push.pushed_at DESC").Offset(offset).Limit(pageSize).Scan(&results).Error; err != nil {
		return nil, 0, errors.New("查询任务列表失败: " + err.Error())
	}

	// 处理结果，补充提问者信息和状态
	statusDescMap := map[string]string{
		"unreplied": "未回复",
		"replied":   "已回复",
		"selected":  "已被选中",
		"expired":   "已过期",
	}

	for i := range results {
		// 计算状态
		status := "unreplied" // 默认未回复
		if results[i]["pushStatus"].(int64) == 1 {
			status = "replied" // 已回复
		}
		if results[i]["selected_reply_id"] != nil && results[i]["replyId"] != nil && results[i]["selected_reply_id"].(uint64) == results[i]["replyId"].(uint64) {
			status = "selected" // 已被选中
		}
		if results[i]["expireTime"].(time.Time).Before(time.Now().In(global.LocTime)) && results[i]["pushStatus"].(int64) != 1 {
			status = "expired" // 已过期且未回复
		}

		// 获取提问者信息
		askerInfo, err := s.getAskerInfo(ctx, results[i]["askerId"].(uint64), results[i]["askerIsAnonymous"].(int64) == 1)
		if err != nil {
			global.GVA_LOG.Error("获取提问者信息失败", zap.Error(err))
			askerInfo = map[string]interface{}{}
		}

		// 组装最终结果
		results[i]["status"] = status
		results[i]["statusDesc"] = statusDescMap[status]
		results[i]["askerInfo"] = askerInfo

		// 删除不需要的字段
		delete(results[i], "askerId")
		delete(results[i], "askerIsAnonymous")
		delete(results[i], "pushStatus")
		delete(results[i], "replyId")
		delete(results[i], "selected_reply_id")
	}

	return results, total, nil
}

// getAskerInfo 获取提问者信息（私有辅助函数）
func (s *AskService) getAskerInfo(ctx context.Context, askerId uint64, isAnonymous bool) (map[string]interface{}, error) {
	db := global.GVA_DB.WithContext(ctx)

	if isAnonymous {
		// 匿名提问：返回匿名信息
		return map[string]interface{}{
			"isAnonymous": 1,
			"nickname":    "匿名用户",
			"avatar":      "",
		}, nil
	}

	// 非匿名提问：获取用户昵称和头像
	var user system.SysUser
	if err := db.Where("id = ?", askerId).First(&user).Error; err != nil {
		return nil, errors.New("查询提问者信息失败: " + err.Error())
	}

	return map[string]interface{}{
		"isAnonymous": 0,
		"nickname":    user.NickName,
		"avatar":      user.HeaderImg,
	}, nil
}

// AcceptAskReply 接受问问回复（标记最佳回复）
// @description: 接受问问回复，标记为最佳回复，并更新任务状态
// @param: ctx context.Context 上下文
// @param: operatorUserId uint64 操作人ID（任务创建者）
// @param: req *request.AcceptAskReplyReq 请求参数
// @return: error 错误信息
func (s *AskService) AcceptAskReply(ctx context.Context, operatorUserId uint64, req *request.AcceptAskReplyReq) error {
	// 开始事务
	err := global.GVA_DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		// 1. 校验任务存在性与权限：确保仅任务创建者可操作
		var task matchs.MatchAskTask
		if err := tx.Where("id = ? AND user_id = ?", req.AskID, operatorUserId).First(&task).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return errors.New("无权操作该任务或任务不存在")
			}
			global.GVA_LOG.Error("查询问问任务失败", zap.Error(err))
			return errors.New("查询任务失败")
		}

		// 2. 校验任务状态：仅能接受'已收到回复'状态的任务回复
		if task.Status != matchs.MatchAskStatusReplied {
			return errors.New("仅能接受'已收到回复'状态的任务回复")
		}

		// 3. 校验回复存在性：确保回复属于该任务
		var reply matchs.MatchAskReply
		if err := tx.Where("id = ? AND task_id = ?", req.ReplyID, req.AskID).First(&reply).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return errors.New("该回复不存在或不属于当前任务")
			}
			global.GVA_LOG.Error("查询回复记录失败", zap.Error(err))
			return errors.New("查询回复失败")
		}

		// 4. 校验是否已接受：检查是否已有最佳回复
		if task.SelectedReplyID > 0 {
			return errors.New("已接受过回复，不可重复操作")
		}

		// 5. 更新回复状态为已接受（使用status字段标记为最佳回复）
		if err := tx.Model(&matchs.MatchAskReply{}).
			Where("id = ?", req.ReplyID).
			Update("status", matchs.MatchAskReplyStatusAccepted).Error; err != nil { // MatchAskReplyStatusAccepted = 1，表示被采纳
			global.GVA_LOG.Error("更新回复状态失败", zap.Error(err))
			return errors.New("更新回复状态失败")
		}

		// 6. 更新任务状态为已接受回复，记录最佳回复ID，并设置完成时间
		now := time.Now().In(global.LocTime)
		taskUpdates := map[string]interface{}{
			"status":            matchs.MatchAskStatusAccepted,
			"selected_reply_id": req.ReplyID,
			"completed_at":      now,
		}

		// 检查是否有completed_at字段，如果有则更新
		if err := tx.Model(&task).Updates(taskUpdates).Error; err != nil {
			global.GVA_LOG.Error("更新任务状态失败", zap.Error(err))
			return errors.New("更新任务状态失败")
		}

		// 7. 创建匹配日志记录
		logPayload := map[string]interface{}{
			"ask_id":           req.AskID,
			"reply_id":         req.ReplyID,
			"operator_user_id": operatorUserId,
		}
		payloadJSON, _ := json.Marshal(logPayload)

		matchLog := matchs.MatchLog{
			TaskType: matchs.MatchRelationTaskTypeAsk,
			TaskID:   uint64(req.AskID),
			UserID:   operatorUserId,
			Action:   matchs.MatchLogActionAskReplyAccepted,
			Payload:  string(payloadJSON),
		}

		if err := tx.Create(&matchLog).Error; err != nil {
			global.GVA_LOG.Error("创建匹配日志失败", zap.Error(err))
			// 日志记录失败不影响主业务
		}

		return nil
	})

	// 事务提交后记录操作日志
	if err == nil {
		global.GVA_LOG.Info("接受问问回复成功",
			zap.Uint64("operator_user_id", operatorUserId),
			zap.Uint("ask_id", req.AskID),
			zap.Uint("reply_id", req.ReplyID),
		)
	}

	return err
}
